/*
 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

/* ScriptData
SDName: Boss_Fathomlord_Karathress
SD%Complete: 70
SDComment: Cyclone workaround
SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
EndScriptData */

#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "serpent_shrine.h"
#include "TemporarySummon.h"

enum FathomlordKarathress
{
    SAY_AGGRO                       = 0,
    SAY_GAIN_BLESSING               = 1,
    SAY_GAIN_ABILITY1               = 2,
    SAY_GAIN_ABILITY2               = 3,
    SAY_GAIN_ABILITY3               = 4,
    SAY_SLAY                        = 5,
    SAY_DEATH                       = 6,

    //Karathress spells
    SPELL_CATACLYSMIC_BOLT          = 38441,
    SPELL_POWER_OF_SHARKKIS         = 38455,
    SPELL_POWER_OF_TIDALVESS        = 38452,
    SPELL_POWER_OF_CARIBDIS         = 38451,
    SPELL_ENRAGE                    = 24318,
    SPELL_SEAR_NOVA                 = 38445,
    SPELL_BLESSING_OF_THE_TIDES     = 38449,

    //Sharkkis spells
    SPELL_LEECHING_THROW            = 29436,
    SPELL_THE_BEAST_WITHIN          = 38373,
    SPELL_MULTISHOT                 = 38366,
    SPELL_SUMMON_FATHOM_LURKER      = 38433,
    SPELL_SUMMON_FATHOM_SPOREBAT    = 38431,
    SPELL_PET_ENRAGE                = 19574,

    //Tidalvess spells
    SPELL_FROST_SHOCK               = 38234,
    SPELL_SPITFIRE_TOTEM            = 38236,
    SPELL_POISON_CLEANSING_TOTEM    = 38306,
    // Spell obsolete
    SPELL_EARTHBIND_TOTEM           = 38304,
    SPELL_EARTHBIND_TOTEM_EFFECT    = 6474,
    SPELL_WINDFURY_WEAPON           = 38184,

    //Caribdis Spells
    SPELL_WATER_BOLT_VOLLEY         = 38335,
    SPELL_TIDAL_SURGE               = 38358,
    SPELL_TIDAL_SURGE_EFFECT        = 38353,
    SPELL_HEAL                      = 38330,
    SPELL_SUMMON_CYCLONE            = 38337,
    SPELL_CYCLONE_CYCLONE           = 29538,

    //Yells and Quotes
    SOUND_MISC                      = 11283,

    //Summoned Unit GUIDs
    CREATURE_CYCLONE                = 22104,
    CREATURE_FATHOM_SPOREBAT        = 22120,
    CREATURE_FATHOM_LURKER          = 22119,
    CREATURE_SPITFIRE_TOTEM         = 22091,
    CREATURE_EARTHBIND_TOTEM        = 22486,
    CREATURE_POISON_CLEANSING_TOTEM = 22487,
};

//entry and position for Seer Olum
#define SEER_OLUM                  22820
#define OLUM_X                     446.78f
#define OLUM_Y                     -542.76f
#define OLUM_Z                     -7.54773f
#define OLUM_O                     0.401581f

#define SAY_MISC                        "Alana be'lendor!" //don't know what use this

#define MAX_ADVISORS 3
//Fathom-Lord Karathress AI
struct boss_fathomlord_karathress : public BossAI
{
    boss_fathomlord_karathress(Creature* creature) : BossAI(creature, BOSS_FATHOM_LORD_KARATHRESS)
    {
        Initialize();
    }

    void Initialize()
    {
        CataclysmicBolt_Timer = 10000;
        Enrage_Timer = 600000;                              //10 minutes
        SearNova_Timer = 20000 + rand32() % 40000; // 20 - 60 seconds

        BlessingOfTides = false;
    }

    uint32 CataclysmicBolt_Timer;
    uint32 Enrage_Timer;
    uint32 SearNova_Timer;

    bool BlessingOfTides;

    ObjectGuid Advisors[MAX_ADVISORS];

    void Reset() override
    {
        Initialize();

        ObjectGuid RAdvisors[MAX_ADVISORS];
        RAdvisors[0] = instance->GetGuidData(DATA_SHARKKIS);
        RAdvisors[1] = instance->GetGuidData(DATA_TIDALVESS);
        RAdvisors[2] = instance->GetGuidData(DATA_CARIBDIS);
        // Respawn of the 3 Advisors
        for (uint8 i = 0; i < MAX_ADVISORS; ++i)
            if (!RAdvisors[i].IsEmpty())
            {
                Creature* advisor = ObjectAccessor::GetCreature(*me, RAdvisors[i]);
                if (advisor && !advisor->IsAlive())
                {
                    advisor->Respawn();
                    advisor->AI()->EnterEvadeMode();
                    advisor->GetMotionMaster()->MoveTargetedHome();
                }
            }

        _Reset();
    }

    void EventSharkkisDeath()
    {
        Talk(SAY_GAIN_ABILITY1);
        DoCast(me, SPELL_POWER_OF_SHARKKIS);
    }

    void EventTidalvessDeath()
    {
        Talk(SAY_GAIN_ABILITY2);
        DoCast(me, SPELL_POWER_OF_TIDALVESS);
    }

    void EventCaribdisDeath()
    {
        Talk(SAY_GAIN_ABILITY3);
        DoCast(me, SPELL_POWER_OF_CARIBDIS);
    }

    void GetAdvisors()
    {
        Advisors[0] = instance->GetGuidData(DATA_SHARKKIS);
        Advisors[1] = instance->GetGuidData(DATA_TIDALVESS);
        Advisors[2] = instance->GetGuidData(DATA_CARIBDIS);
    }

    void StartEvent(Unit* who)
    {
        GetAdvisors();

        Talk(SAY_AGGRO);

        instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
    }

    void KilledUnit(Unit* /*victim*/) override
    {
        Talk(SAY_SLAY);
    }

    void JustDied(Unit* /*killer*/) override
    {
        Talk(SAY_DEATH);

        instance->SetData(DATA_FATHOMLORDKARATHRESSEVENT, DONE);

        //support for quest 10944
        me->SummonCreature(SEER_OLUM, OLUM_X, OLUM_Y, OLUM_Z, OLUM_O, TEMPSUMMON_TIMED_DESPAWN, 1h);
    }

    void JustEngagedWith(Unit* who) override
    {
        _JustEngagedWith(who);
        StartEvent(who);
    }

    void UpdateAI(uint32 diff) override
    {
        //Only if not incombat check if the event is started
        if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
        {
            if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
            {
                AttackStart(target);
                GetAdvisors();
            }
        }

        //Return since we have no target
        if (!UpdateVictim())
            return;

        //someone evaded!
        if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
        {
            EnterEvadeMode();
            return;
        }

        //CataclysmicBolt_Timer
        if (CataclysmicBolt_Timer <= diff)
        {
            //select a random unit other than the main tank
            Unit* target = SelectTarget(SelectTargetMethod::Random, 1);

            //if there aren't other units, cast on the tank
            if (!target)
                target = me->GetVictim();

            if (target)
                DoCast(target, SPELL_CATACLYSMIC_BOLT);
            CataclysmicBolt_Timer = 10000;
        } else CataclysmicBolt_Timer -= diff;

        //SearNova_Timer
        if (SearNova_Timer <= diff)
        {
            DoCastVictim(SPELL_SEAR_NOVA);
            SearNova_Timer = 20000 + rand32() % 40000;
        } else SearNova_Timer -= diff;

        //Enrage_Timer
        if (Enrage_Timer <= diff)
        {
            DoCast(me, SPELL_ENRAGE);
            Enrage_Timer = 90000;
        } else Enrage_Timer -= diff;

        //Blessing of Tides Trigger
        if (!HealthAbovePct(75) && !BlessingOfTides)
        {
            BlessingOfTides = true;
            bool continueTriggering = false;
            for (uint8 i = 0; i < MAX_ADVISORS; ++i)
                if (!Advisors[i].IsEmpty())
                {
                    Creature* advisor = ObjectAccessor::GetCreature(*me, Advisors[i]);
                    if (advisor && advisor->IsAlive())
                    {
                        continueTriggering = true;
                        break;
                    }
                }
            if (continueTriggering)
            {
                DoCast(me, SPELL_BLESSING_OF_THE_TIDES);
                Talk(SAY_GAIN_BLESSING);
            }
        }
    }
};

//Fathom-Guard Sharkkis AI
struct boss_fathomguard_sharkkis : public ScriptedAI
{
    boss_fathomguard_sharkkis(Creature* creature) : ScriptedAI(creature)
    {
        Initialize();
        instance = creature->GetInstanceScript();
    }

    void Initialize()
    {
        LeechingThrow_Timer = 20000;
        TheBeastWithin_Timer = 30000;
        Multishot_Timer = 15000;
        Pet_Timer = 10000;

        pet = false;
    }

    InstanceScript* instance;

    uint32 LeechingThrow_Timer;
    uint32 TheBeastWithin_Timer;
    uint32 Multishot_Timer;
    uint32 Pet_Timer;

    bool pet;

    ObjectGuid SummonedPet;

    void Reset() override
    {
        Initialize();

        Creature* Pet = ObjectAccessor::GetCreature(*me, SummonedPet);
        if (Pet && Pet->IsAlive())
            Pet->KillSelf();

        SummonedPet.Clear();

        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
    }

    void JustDied(Unit* /*killer*/) override
    {
        if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
            ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventSharkkisDeath();
    }

    void JustEngagedWith(Unit* who) override
    {
        instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
    }

    void UpdateAI(uint32 diff) override
    {
        //Only if not incombat check if the event is started
        if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
        {
            if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
                AttackStart(target);
        }

        //Return since we have no target
        if (!UpdateVictim())
            return;

        //someone evaded!
        if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
        {
            EnterEvadeMode();
            return;
        }

        //LeechingThrow_Timer
        if (LeechingThrow_Timer <= diff)
        {
            DoCastVictim(SPELL_LEECHING_THROW);
            LeechingThrow_Timer = 20000;
        } else LeechingThrow_Timer -= diff;

        //Multishot_Timer
        if (Multishot_Timer <= diff)
        {
            DoCastVictim(SPELL_MULTISHOT);
            Multishot_Timer = 20000;
        } else Multishot_Timer -= diff;

        //TheBeastWithin_Timer
        if (TheBeastWithin_Timer <= diff)
        {
            DoCast(me, SPELL_THE_BEAST_WITHIN);

            Creature* Pet = ObjectAccessor::GetCreature(*me, SummonedPet);
            if (Pet && Pet->IsAlive())
                Pet->CastSpell(Pet, SPELL_PET_ENRAGE, true);

            TheBeastWithin_Timer = 30000;
        } else TheBeastWithin_Timer -= diff;

        //Pet_Timer
        if (Pet_Timer < diff && pet == false)
        {
            pet = true;
            //uint32 spell_id;
            uint32 pet_id;
            if (!urand(0, 1))
            {
                //spell_id = SPELL_SUMMON_FATHOM_LURKER;
                pet_id = CREATURE_FATHOM_LURKER;
            }
            else
            {
                //spell_id = SPELL_SUMMON_FATHOM_SPOREBAT;
                pet_id = CREATURE_FATHOM_SPOREBAT;
            }
            //DoCast(me, spell_id, true);
            if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
            {
                if (Creature* Pet = DoSpawnCreature(pet_id, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15s))
                {
                    Pet->AI()->AttackStart(target);
                    SummonedPet = Pet->GetGUID();
                }
            }
        } else Pet_Timer -= diff;
    }
};

//Fathom-Guard Tidalvess AI
struct boss_fathomguard_tidalvess : public ScriptedAI
{
    boss_fathomguard_tidalvess(Creature* creature) : ScriptedAI(creature)
    {
        Initialize();
        instance = creature->GetInstanceScript();
    }

    void Initialize()
    {
        FrostShock_Timer = 25000;
        Spitfire_Timer = 60000;
        PoisonCleansing_Timer = 30000;
        Earthbind_Timer = 45000;
    }

    InstanceScript* instance;

    uint32 FrostShock_Timer;
    uint32 Spitfire_Timer;
    uint32 PoisonCleansing_Timer;
    uint32 Earthbind_Timer;

    void Reset() override
    {
        Initialize();

        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
    }

    void JustDied(Unit* /*killer*/) override
    {
        if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
            ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventTidalvessDeath();
    }

    void JustEngagedWith(Unit* who) override
    {
        instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
        DoCast(me, SPELL_WINDFURY_WEAPON);
    }

    void UpdateAI(uint32 diff) override
    {
        //Only if not incombat check if the event is started
        if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
        {
            if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
                AttackStart(target);
        }

        //Return since we have no target
        if (!UpdateVictim())
            return;

        //someone evaded!
        if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
        {
            EnterEvadeMode();
            return;
        }

        if (!me->HasAura(SPELL_WINDFURY_WEAPON))
        {
            DoCast(me, SPELL_WINDFURY_WEAPON);
        }

        //FrostShock_Timer
        if (FrostShock_Timer <= diff)
        {
            DoCastVictim(SPELL_FROST_SHOCK);
            FrostShock_Timer = 25000 + rand32() % 5000;
        } else FrostShock_Timer -= diff;

        //Spitfire_Timer
        if (Spitfire_Timer <= diff)
        {
            DoCast(me, SPELL_SPITFIRE_TOTEM);
            if (Unit* SpitfireTotem = me->FindNearestCreature(CREATURE_SPITFIRE_TOTEM, 100.0f))
                SpitfireTotem->ToCreature()->AI()->AttackStart(me->GetVictim());

            Spitfire_Timer = 60000;
        }
        else
            Spitfire_Timer -= diff;

        //PoisonCleansing_Timer
        if (PoisonCleansing_Timer <= diff)
        {
            DoCast(me, SPELL_POISON_CLEANSING_TOTEM);
            PoisonCleansing_Timer = 30000;
        }
        else
            PoisonCleansing_Timer -= diff;

        //Earthbind_Timer
        if (Earthbind_Timer <= diff)
        {
            DoCast(me, SPELL_EARTHBIND_TOTEM);
            Earthbind_Timer = 45000;
        }
        else
            Earthbind_Timer -= diff;
    }
};

//Fathom-Guard Caribdis AI
struct boss_fathomguard_caribdis : public ScriptedAI
{
    boss_fathomguard_caribdis(Creature* creature) : ScriptedAI(creature)
    {
        Initialize();
        instance = creature->GetInstanceScript();
    }

    void Initialize()
    {
        WaterBoltVolley_Timer = 35000;
        TidalSurge_Timer = 15000 + rand32() % 5000;
        Heal_Timer = 55000;
        Cyclone_Timer = 30000 + rand32() % 10000;
    }

    InstanceScript* instance;

    uint32 WaterBoltVolley_Timer;
    uint32 TidalSurge_Timer;
    uint32 Heal_Timer;
    uint32 Cyclone_Timer;

    void Reset() override
    {
        Initialize();

        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, NOT_STARTED);
    }

    void JustDied(Unit* /*killer*/) override
    {
        if (Creature* Karathress = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_KARATHRESS)))
            ENSURE_AI(boss_fathomlord_karathress, Karathress->AI())->EventCaribdisDeath();
    }

    void JustEngagedWith(Unit* who) override
    {
        instance->SetGuidData(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
        instance->SetBossState(BOSS_FATHOM_LORD_KARATHRESS, IN_PROGRESS);
    }

    void UpdateAI(uint32 diff) override
    {
        //Only if not incombat check if the event is started
        if (!me->IsInCombat() && instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == IN_PROGRESS)
        {
            if (Unit* target = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESSEVENT_STARTER)))
                AttackStart(target);
        }

        //Return since we have no target
        if (!UpdateVictim())
            return;

        //someone evaded!
        if (instance->GetBossState(BOSS_FATHOM_LORD_KARATHRESS) == NOT_STARTED)
        {
            EnterEvadeMode();
            return;
        }

        //WaterBoltVolley_Timer
        if (WaterBoltVolley_Timer <= diff)
        {
            DoCastVictim(SPELL_WATER_BOLT_VOLLEY);
            WaterBoltVolley_Timer = 30000;
        } else WaterBoltVolley_Timer -= diff;

        //TidalSurge_Timer
        if (TidalSurge_Timer <= diff)
        {
            DoCastSelf(SPELL_TIDAL_SURGE);
            TidalSurge_Timer = 15000 + rand32() % 5000;
        } else TidalSurge_Timer -= diff;

        //Cyclone_Timer
        if (Cyclone_Timer <= diff)
        {
            //DoCast(me, SPELL_SUMMON_CYCLONE); // Doesn't work
            Cyclone_Timer = 30000 + rand32() % 10000;

            if (Creature* Cyclone = me->SummonCreature(CREATURE_CYCLONE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), float(rand32() % 5), TEMPSUMMON_TIMED_DESPAWN, 15s))
            {
                Cyclone->SetObjectScale(3.0f);
                Cyclone->SetFaction(me->GetFaction());
                Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_CYCLONE, true);
                if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
                    Cyclone->AI()->AttackStart(target);
            }
        }
        else
            Cyclone_Timer -= diff;

        //Heal_Timer
        if (Heal_Timer <= diff)
        {
            // It can be cast on any of the mobs
            Unit* unit = nullptr;

            while (unit == nullptr || !unit->IsAlive())
                unit = selectAdvisorUnit();

            DoCast(unit, SPELL_HEAL);
            Heal_Timer = 60000;
        }
        else
            Heal_Timer -= diff;
    }

    Unit* selectAdvisorUnit()
    {
        Unit* unit = nullptr;
        switch (rand32() % 4)
        {
        case 0:
            unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_KARATHRESS));
            break;
        case 1:
            unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_SHARKKIS));
            break;
        case 2:
            unit = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_TIDALVESS));
            break;
        case 3:
            unit = me;
            break;
        }
        return unit;
    }
};

// 38358 - Tidal Surge
class spell_fathomlord_karathress_tidal_surge : public SpellScript
{
    bool Validate(SpellInfo const* /*spellInfo*/) override
    {
        return ValidateSpellInfo({ SPELL_TIDAL_SURGE_EFFECT });
    }

    void HandleScript(SpellEffIndex /*effIndex*/)
    {
        GetHitUnit()->CastSpell(GetHitUnit(), SPELL_TIDAL_SURGE_EFFECT, true);
    }

    void Register() override
    {
        OnEffectHitTarget += SpellEffectFn(spell_fathomlord_karathress_tidal_surge::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
    }
};

void AddSC_boss_fathomlord_karathress()
{
    RegisterSerpentshrineCavernCreatureAI(boss_fathomlord_karathress);
    RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_sharkkis);
    RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_tidalvess);
    RegisterSerpentshrineCavernCreatureAI(boss_fathomguard_caribdis);
    RegisterSpellScript(spell_fathomlord_karathress_tidal_surge);
}
